// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: donggu

#include "coin2/exchange/factory/order/system.h"

#include <memory>

#include "coin2/exchange/base/order/processor.h"
#include "coin2/exchange/factory/order/system_components.h"
#include "coin2/exchange/sim/order_v1/subsystem.h"

namespace coin2::exchange::factory::order {

using OrderSystemConfig = coin2::exchange::order::OrderSystemConfig;
using PrivateSubscriberConfig = coin2::exchange::order::PrivateSubscriberConfig;
using GetProductHolder = std::function<
    const coin2::exchange::base::symbology::IProductHolder*(
        const coin2::exchange::base::symbology::IProduct&, int64_t)>;

PrivateSubscriberConfig GetSubscriberConfig(
    const MarketExchangeApi& mea,
    const OrderSystemConfig& config) {
  PrivateSubscriberConfig default_subscriber_config;
  std::ifstream ifs("data/coin2/order/og_info.json");
  nlohmann::json jf = nlohmann::json::parse(ifs);
  if (jf.find(mea.String()) != jf.end()) {
    auto data = jf[mea.String()];
    if (data.find("query_account_balance_period_sec") != data.end()) {
      default_subscriber_config.set_query_account_balance_period_sec(
          data["query_account_balance_period_sec"]);
    }
    if (data.find("query_account_position_period_sec") != data.end()) {
      default_subscriber_config.set_query_account_position_period_sec(
          data["query_account_position_period_sec"]);
    }
    if (data.find("query_open_orders_period_sec") != data.end()) {
      default_subscriber_config.set_query_open_orders_period_sec(
          data["query_open_orders_period_sec"]);
    }
    if (data.find("query_fill_period_sec") != data.end()) {
      default_subscriber_config.set_query_fill_period_sec(data["query_fill_period_sec"]);
    }
    if (data.find("query_cancel_orders_period_sec") != data.end()) {
      default_subscriber_config.set_query_cancel_orders_period_sec(
          data["query_cancel_orders_period_sec"]);
    }
  }
  default_subscriber_config.MergeFrom(config.rest_query_config());
  LOG(INFO) << "[SubscriberConfig]\n" << default_subscriber_config.DebugString();
  return default_subscriber_config;
}

base::order::BaseOrderSubsystem* CreateOrderSystem(
    const std::string& name,
    const MarketExchangeApi& mea,
    const di::ConstantInjection& ci,
    const OrderSystemConfig& config,
    base::executor::LiveAsyncExecutor* live_executor,
    base::order::IOrderSubscriber* callback,
    GetProductHolder get_product_holder) {
  auto os = new base::order::BaseOrderSubsystem(name, mea, ci, config, live_executor, callback, get_product_holder);
  OrderSubsystemComponents component;
  component.Create(mea, config, GetSubscriberConfig(mea, config), live_executor, os->http_driver());
  os->SetParser(component.parser());
  os->SetRestSubscriber(component.rest_subscriber());
  os->SetRestClient(component.rest_client());
  os->LiveInit(config, live_executor);
  LOG(INFO) << "[BaseOrderSystem Created!]" << mea.String();
  return os;
}

base::order::IOrderSubsystem* CreateSimOrderSystem(
    const std::string& name,
    const MarketExchangeApi& mea,
    const di::ConstantInjection& ci,
    const OrderSystemConfig& config,
    base::executor::ArchiveSyncExecutor* exec,
    coin2::feed::mgr::FeedSubsystemManager* fsm,
    base::order::IOrderSubscriber* callback,
    GetProductHolder get_product_holder) {
  sim::order_v1::SimSubsystem* sim_subsystem =
      new sim::order_v1::SimSubsystem(
          name, mea, ci, config, exec, callback, GetSubscriberConfig(mea, config), get_product_holder);
  sim_subsystem->ConnectFeed(fsm);
  return sim_subsystem;
}

}  // namespace coin2::exchange::factory::order
